package xdb.util;

import mysql.MySqlMgr;

import java.io.File;
import java.util.HashSet;
import java.util.Set;

/**
 * <p>Xdb 正在进行checkpoint时，程序异常停止（包括机器异常关机），数据有可能处于不一致状态。
 * 在继续使用数据库时，需要修复。xdb会自动检测和尝试修复不一致状态，对于xdb应用不需要关心这个问题。
 * 如果在修复之前使用Dbx或者其他工具去访问数据，就会造成数据损坏。
 *
 * <p><b>这个工具类用来修复这种不一致状态。</b> Dbx 在打开时，会使用它检查和修复数据状态。
 *
 * <p>本类可以独立使用。
 */
public class Integrity {
    private DatabaseMetaData metaData = null;
    private final File tableHome;

    /**
     * 根据指定数据库目录验证数据完整性。
     * 同时验证DatabaseMetaData包含的表格和数据目录中包含的表格文件一致。
     *
     * <p> <b>只有在确定表格没有增删时，才需要验证DatabaseMetaData。</b>
     *
     * @param libpath  动态库所在路径。动态库已经在外面装在，设为null。
     * @param home
     * @param metaData 为null时，不验证DatabaseMetaData的信息。
     */
    public Integrity(File home, DatabaseMetaData metaData) {

        if (!home.isDirectory() || !home.exists())
            throw new IllegalStateException("dbhome : " + home + " (!isDirectory || !exists())");

        this.tableHome = home;
        this.metaData = metaData;
    }

    /**
     * 完整性检查和修复。
     */
    public void verify() {
        ////////////////////////////////////////////////////////////////
        // 检测数据库目录
        int magic = 0;
        if (this.tableHome.exists()) magic += 2;

        switch (magic) {
            // 数据和日志目录都不存在。一般来说，这是空的数据库，认为是正确的。
            case 0:
                println("ok! it is an empty db.");
                return;
            // 不是正确的数据目录。
            case 1:
                throw new RuntimeException("is it a dbhome?");
                // 好吧，目录都在。
            case 2:
                break;
        }

        ////////////////////////////////////////////////////////////////
        // 验证MetaData信息和目录中包含的表格文件一致。
        final Set<String> tableNames = MySqlMgr.GetInstance().GetTableNames();
        if (null != this.metaData) {
            final Set<String> tableMetas = new HashSet<String>();
            for (DatabaseMetaData.Table table : metaData.getTables()) {
                // 忽略内存表
                if (table.isPersistence()) {
                    tableMetas.add(table.getName());
                }

                if (tableNames.contains(table.getName()) == false) {
                    throw new RuntimeException("Table " + table.getName() + " is unexist in MySql, but exist in metaData file!");
                }
            }
        }
    }

    private void println(Object message) {
        System.out.println(message);
    }
}
